package crypto

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"github.com/google/uuid"
	"golang.org/x/crypto/bcrypt"
	"io"
	"sync"
)

type Crypto struct {
	Id     uuid.UUID // 生成uuid
	MD5    string    // 生成md5
	Hex    string    // hmac加密
	Code   string
	mu     sync.Mutex
	workId int64
}

// 初始化加密方法，生成uuid
func Init() *Crypto {
	id, err := uuid.NewV7()
	if err != nil {
		return &Crypto{}
	}
	return &Crypto{
		Id: id,
	}
}

// 通过uuid和其余参数生成md5
func (cry *Crypto) GetIds(value any) *Crypto {
	md := md5.New()
	pd := fmt.Sprintf("%v_%v", cry.Id, value)
	if _, err := io.WriteString(md, pd); err != nil {
		return cry
	}
	cry.MD5 = hex.EncodeToString(md.Sum(nil))
	return cry
}

// 根据传入参数生产md
func (cry *Crypto) GetMD5(val string) *string {
	md := md5.New()
	if _, err := io.WriteString(md, val); err != nil {
		return nil
	}
	d5 := hex.EncodeToString(md.Sum(nil))
	return &d5
}

// 对两个数据进行校验
func Verify(val string, value string) bool {
	if err := bcrypt.CompareHashAndPassword([]byte(val), []byte(value)); err != nil {
		return false
	}
	return true
}

// 两个数据是否相同
func Same(val, value string) bool {
	return val == value
}

// 转为base64

func (cry *Crypto) Encode(value string) *Crypto {
	txt := fmt.Sprintf("%v_%s", cry.Id, value)
	cry.Code = value
	cry.Hex = base64.StdEncoding.EncodeToString([]byte(txt))
	return cry
}

// 解码为字符串

func (cry *Crypto) Decode(value string) string {
	txt, err := base64.StdEncoding.DecodeString(value)
	if err != nil {
		return err.Error()
	}
	return string(txt)
}

// hmac加密

func (cry *Crypto) DeHmac(value string) *Crypto {
	hma := hmac.New(md5.New, []byte(cry.Id.String()))
	hma.Write([]byte(value))
	cry.Hex = hex.EncodeToString(hma.Sum([]byte(value)))
	return cry
}

// PKCS7 填充模式
func cs7Padding(txt []byte, size int) []byte {
	padding := size - len(txt)%size
	val := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(txt, val...)
}

// 填充的反向操作，删除填充字符串
func cs7UnPadding(list []byte) []byte {
	//获取数据长度
	length := len(list)
	if length == 0 {
		return nil
	} else {
		//获取填充字符串长度
		padding := int(list[length-1])
		//截取切片，删除填充字节，并且返回明文
		return list[:(length - padding)]
	}
}

// aes加密
func (cry *Crypto) AesEncode(value string) *Crypto {
	id := []byte(cry.Id.String())
	val := []byte(value)
	cip, err := aes.NewCipher(id)
	if err != nil {
		return cry
	}
	size := cip.BlockSize()
	val = cs7Padding(val, size)
	mode := cipher.NewCBCEncrypter(cip, id[:size])
	list := make([]byte, len(val))
	mode.CryptBlocks(list, val)
	cry.Hex = hex.EncodeToString(list)
	return cry
}

// aes解密
func (cry *Crypto) AesDecode(opts *Crypto) bool {
	bit, err := hex.DecodeString(opts.Hex)
	if err != nil {
		return false
	}
	id := []byte(opts.Id.String())
	cip, err := aes.NewCipher(id)
	if err != nil {
		return false
	}
	size := cip.BlockSize()
	mode := cipher.NewCBCEncrypter(cip, id[:size])
	list := make([]byte, len(bit))
	mode.CryptBlocks(list, bit)
	list = cs7UnPadding(list)
	if list == nil {
		return false
	}
	cry.Code = string(list)
	return true
}
